/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.swing;

import java.awt.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.BoxLayout;

/**
 * Important: This panel works for the most part but will need more work.
 *  Its mostly exists to demonstrate that a Swing version of an AxisPanel is
 *  possible.
 *  
 * TODO: Add more space() alternatives.
 * 
 * @author Sony Mathew
 */
public class AxisPanel extends AxisPanelBox {

    /**
     * Box used when laying out in the X Axis.
     */
    public static class XBox extends AxisPanelBox {
        public XBox() {
            super(BoxLayout.X_AXIS);
        }
    }
    
    /**
     * Box used when laying out in the Y Axis.
     */    
    public static class YBox extends AxisPanelBox {
        public YBox() {
            super(BoxLayout.Y_AXIS);
        }
    }
    
    /**
     * A target for Box operations.
     */
    static class Target {
        Target parent;           
        AxisPanelBox box;
        List<Component> children;
        
        Target(Target parentTarget, AxisPanelBox target, Component...children) {
            this.parent = parentTarget;
            this.box = target;
            this.children = new ArrayList<Component>(Arrays.asList(children));
        }
        
        /**
         * Don't add child Components directly to the box, add it to the children 
         *  and then call commit() when ready.
         *  
         * Adds all the children collected so far into the box for real.
         *   and then add self to parent's children list.
         *   
         * Returns the parent - the next uncommited target.
         * 
         * author Sony Mathew
         */
        Target commit() {
            for(Component child : children) {
                box.add(child);
            }
            children = new ArrayList<Component>(); //can build new set of children

            if (parent != null) {
                parent.children.add(box);
            }
                        
            return parent;
        }
    }
    
    private Target target = new Target(null, this);
    
    /**
     * To Start the AxisPanel lays out on a Y-Axis.
     * 
     * author Sony Mathew
     */
    public AxisPanel() {
        super(BoxLayout.Y_AXIS);
    }
    
    /**
     * Create an X axis panel and layout the given components in it.
     * 
     * author Sony Mathew
     */
    public AxisPanel x(Component...cs) {
        return changeTarget(new XBox(), cs);
    }

    /**
     * Create an Y axis panel and layout the given components in it.
     * 
     * author Sony Mathew
     */
    public AxisPanel y(Component...cs) {
        return changeTarget(new YBox(), cs);
    }
    
    /**
     * Done building the current or target Box.
     * 
     * author Sony Mathew
     */
    public AxisPanel q() {
        if (target.box == this) {
            throw new IllegalStateException("Cannot close the root AxisPanel");
        }
         
        target = target.commit();
        
        if (target.box == this) {
            target.commit();
        }
        
        return this;
    }
    
    /**
     * Adds the given space to both sides of every component in the box.
     * 
     *  e.g
     *  For X axis box, space is added the Left and Right sides of each component.
     *  For Y axis box, space is added to the Top and Bottom sides of each component.
     *  
     *  Note: space between components is added only once i.e. space between
     *  components at index 2 and 3 is going to be just space not space*2.
     *  
     * author Sony Mathew
     */
    public AxisPanel space(int space) {  
        List<Component> newChildren = new ArrayList<Component>();
        newChildren.add(createAxisStrut(space));
        for(Component targetChild : target.children){
            newChildren.add(targetChild);
            newChildren.add(createAxisStrut(space));            
        }
        target.children = newChildren;
        return this;
    }
    
    /**
     * Iterates thru parents of given component to find a suitable parent to 
     *  become box.
     *  
     *  Throws an exception if no suitable paretn box could be found.
     * 
     * author Sony Mathew
     */
    public AxisPanelBox findParentTarget(Component c) {
        while((c=c.getParent()) != null) {
            if (c instanceof AxisPanelBox) {
                return (AxisPanelBox)c;
            }
        }        
        
        throw new IllegalStateException("Error: Cannot find a suitable parent");
    }
    
    /**
     * Changes the box, its parent and its children to the given.
     *   
     * author Sony Mathew
     */
    private AxisPanel changeTarget(AxisPanelBox newTargetBox, Component...newTargetChildren) {
        target = new Target(target, newTargetBox, newTargetChildren);
        return this;        
    }
    
}
